/*
 * Client.hpp
 *
 *  Created on: 07/lug/2009
 *      Author: Asteroth
 */

#ifndef __ENG_CLIENT_HPP__
#define __ENG_CLIENT_HPP__

#include <winsock2.h>
#include <winsock.h>
#include <cstdlib>
#include <vector>
#include <memory>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

#include <estdlib/String.hpp>

#include "NetworkUtils.hpp"

namespace engine {
namespace net {

class TCPClient {
private:
	boost::asio::io_service* service;
	boost::asio::ip::tcp::socket* socket;

	char* data;
	int length;

	void receiveHandler(const boost::system::error_code &error, boost::asio::ip::tcp::socket *socket, size_t bytes_transferred) {
		if (isConnected()) {
			if (!error) {
				onReceive(socket, data, bytes_transferred);
				socket->async_receive(boost::asio::buffer(data, length), boost::bind(&TCPServer::receiveHandler, this, boost::asio::placeholders::error, socket, boost::asio::placeholders::bytes_transferred));
			} else {
				onDisconnect(socket);
				for (unsigned int i = 0; i < sockets.size(); i++)
					if (sockets[i] == socket) {
						delete sockets[i];
						sockets[i] = NULL;
						break;
					}
			}
		}
	}

	void sendHandler(const boost::system::error_code &error, boost::asio::ip::tcp::socket *socket, size_t bytes_transferred) {
		if (isConnected()) {
			if (!error) {
				onSendSuccess(socket);
			} else {
				onSendFailure(socket);
				socket->close();
			}
		}
	}

public:
	TCPClient(estd::String ip, int port, int ipVersion = 4, int bufferLength = 1024) {
		service = new boost::asio::io_service();
		socket = new boost::asio::ip::tcp::socket(*service);

		if (bufferLength < 1)
			bufferLength = 1;
		length = bufferLength;
		data = new char[length];

		boost::asio::ip::tcp::resolver resolver(*service);
		boost::asio::ip::tcp::resolver::query query((ipVersion == 6? boost::asio::ip::tcp::v6(): boost::asio::ip::tcp::v4()), ip, estd::String::fromInt(port));
		boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
		socket->connect(*iterator);
		socket->async_receive(boost::asio::buffer(data, length), boost::bind(&TCPServer::receiveHandler, this, boost::asio::placeholders::error, socket, boost::asio::placeholders::bytes_transferred));
	}
	virtual ~TCPClient() {
		socket->close();
		delete socket;

		service->stop();
		delete service;
	}

	bool isConnected() const {return socket.is_open();}

	int getBufferLength() {return length;}
	void setBufferLength(int newLength) {
		if (newLength < 1)
			newLength = 1;
		length = newLength;
		delete[] data;
		data = new char[length];
	}

	bool run() {
		try {
			service->run();
			return true;
		} catch (std::exception &e) {
			return false;
		}
	}

	virtual void onConnectSuccess(boost::asio::ip::tcp::socket* socket) = 0;
	virtual void onConnectFailure(boost::asio::ip::tcp::socket* socket) = 0;
	virtual void onDisconnect(boost::asio::ip::tcp::socket* socket) = 0;
	virtual void onSendSuccess(boost::asio::ip::tcp::socket* socket) = 0;
	virtual void onSendFailure(boost::asio::ip::tcp::socket* socket) = 0;
	virtual void onReceive(boost::asio::ip::tcp::socket* socket, char* data, int length) = 0;
};

}}

#endif
